package com.ocom.srvpamh.security;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ocom.common.utils.DateUtil;
import com.ocom.common.utils.JsonResponse;
import com.ocom.common.utils.MD5;
import com.ocom.common.utils.StateCode;
import com.ocom.redis.util.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;

@Aspect
@Component
public class ServiceAspect {

    private Logger log = LoggerFactory.getLogger(ServiceAspect.class);

    @Resource
    private RedisUtil redisUtil;

    //切入点
    @Around("execution(* com.ocom.srvpamh.controller.MachineController.*(..))")
    //增强
    public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {

        Map<String,Object> reqParam =null;
        String url ="";
        String machno="";
        try {
            long  startTimeMillis = System.currentTimeMillis();
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            //IP地址
            String ipAddr = getRemoteHost(request);
            url = request.getRequestURL().toString();
            Object[] args = pjp.getArgs();
            String strUrl = url.substring(url.lastIndexOf("/")+1);
            if (strUrl.equals("SetDeviceActBySaas") || strUrl.equals("SetDeviceUnActBySaas")){
                return pjp.proceed(args);
            }
            reqParam = preHandle(args);

            //机器号
            machno= String.valueOf(reqParam.get("machNo"));
            //验签
            if (!requestJSON(reqParam,request)){
                JsonResponse jsonResponse=JsonResponse.error(StateCode.signCod,StateCode.signMsg);
                log.info("请求URL:【{}】,机器号:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,reqParam,jsonResponse);
                return jsonResponse;
            }
            //判断请求的时间是否在5分钟内，不在5分钟内的拒绝访问。离线的支付接口不受这个限制
            if (!strUrl.equals("TradeRecord")){
                String Ts = request.getHeader("Ts");
                long longTime= Long.parseLong(Ts);
                if (!DateUtil.timeStamp(longTime)){
                    JsonResponse jsonResponse=JsonResponse.error(StateCode.timeCod,StateCode.timeMsg);
                    log.info("请求URL:【{}】,机器号:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,reqParam,jsonResponse);
                    return jsonResponse;
                }
            }
            Object result=null;
            //判断请求的地址是否是签到，是签到的不需要从redis获取商家id
            if (strUrl.equals("Sign")){
                // 执行拦截方法
                result= pjp.proceed(args);
            }else {
                try{
                    Integer saasNo  = (Integer) redisUtil.hmGet("HttpMjDevice:" + machno, "saasNo");
                    if(saasNo == 10 || saasNo==11){
                        if (!strUrl.equals("Heartbeat") && !strUrl.equals("UpgradeFinish")) {
                            return JsonResponse.error(StateCode.signCod, StateCode.signMsg);
                        }
                        Object objectCompanyId=redisUtil.hmGet("HttpMjDevice:"+machno,"companyId");
                        Integer companyId= (Integer) objectCompanyId;
                        return pjp.proceed(agrValue(args,companyId,ipAddr));
                    }
                }catch (Exception e){
                    log.info("心跳异常 【】",e);
                }


                //获取redis的companyId的值
                Object objectCompanyId=redisUtil.hmGet("HttpMjDevice:"+machno,"companyId");
                if (objectCompanyId==null){
                    JsonResponse jsonResponse=JsonResponse.error(StateCode.companyIdReidsCod,StateCode.companyIdReidsMsg);
                    log.info("请求URL:【{}】,机器号:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,reqParam,jsonResponse);
                    return jsonResponse;
                }
                Integer companyId= (Integer) objectCompanyId;
                //获取redis的sessionId的值
                Object objectSessionId=redisUtil.hmGet("HttpMjDevice:"+machno,"sessionID");
                if (reqParam.get("sessionId")==null || objectSessionId==null){
                    JsonResponse jsonResponse=JsonResponse.error(StateCode.sessionIdCod,StateCode.sessionIdMsg);
                    log.info("请求URL:【{}】,机器号:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,reqParam,jsonResponse);
                    return jsonResponse;
                }else {
                    String reqSessionId= (String) reqParam.get("sessionId");
                    String redisSessionId= (String) objectSessionId;
                    if (reqSessionId.equals(redisSessionId)){
                        // 执行拦截方法
                        result= pjp.proceed(agrValue(args,companyId,ipAddr));
                    }else {
                        JsonResponse jsonResponse=JsonResponse.error(StateCode.sessionIdCod,StateCode.sessionIdMsg);
                        log.info("请求URL:【{}】,机器号:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,reqParam,jsonResponse);
                        return jsonResponse;
                    }
                }
            }
            int timeValue = Math.toIntExact(System.currentTimeMillis() - startTimeMillis);
            result = changValue(result,timeValue);
            logdet(url,machno,reqParam,result,strUrl);
            return result;
        }catch (Exception e){
            e.printStackTrace();
            log.info("请求URL:【{}】,机器号:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,reqParam,e.getMessage());
            return JsonResponse.error();
        }
    }


    private boolean requestJSON(Map<String, Object> map,HttpServletRequest req){
        String Ts = req.getHeader("Ts");
        String Sign = req.getHeader("Sign");
        if (map==null){
            return false;
        }
        if (Ts==null || Sign==null){
            return false;
        }
        map.put("ts",Ts);
        SortedMap<String, Object> parameters = new TreeMap<String, Object>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key=entry.getKey().toLowerCase();
            parameters.put(key,entry.getValue());
        }
        String strEncryption=createSign(parameters,"2D7E7C96-DAC5-4526-96C3-C60CDEC4B120");
//        log.info("Sign:【{}】,Ts:【{}】,验签请求的参数:【{}】,加密后的签名:【{}】",Sign,Ts,map,strEncryption);
        if (Sign.equals(strEncryption)){
            return true;
        }else {
            System.out.println("==============="+"false");
            return false;
        }
    }

    private  String createSign(SortedMap<String,Object> parameters,String key){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序）
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
//                log.info("加密参数:#循环中key【{}】,value【{}",k,v);
                sb.append(k + "=" + v + "&");
            }
        }

        sb=sb.append("key="+key);
        //MD5加密,结果转换为大写字符
        //String sign = MD5Util.MD5Encode(sbkey.toString(), characterEncoding).toUpperCase();
//        String sign = MD5Util.stringToMD5(sb.toString());
        String sign = MD5.MD5Encode(sb.toString(),"utf8");
//        log.info("加密参数:【{}】,加密前的字符串:【{}】,加密后的签名:【{}】,key:【{}】",parameters,sb.toString(),sign,key);
        return sign;
    }
    /*
     * @Description: 打印日志
     * @Author: lu
     * @Date: 2022/4/7 15:40
     * @param machno:
     * @param reqParam:
     * @param result:
     * @param strUrl:
     * @return: void
     **/
    private void logdet(String url,String machno,Map<String,Object> reqParam,Object result,String strUrl){
        String method="";
        if (strUrl.equals("Sign")){
            method="门禁的签到";
        }else if (strUrl.equals("Heartbeat")){
            method="门禁的心跳";
        }else if (strUrl.equals("MachinePara")){
            method="门禁的设备参数";
        }else if (strUrl.equals("MachineParaResult")){
            method="设备参数通知";
        }else if (strUrl.equals("EntranceRecord")){
            method="上传门禁记录";
        }else if (strUrl.equals("JKMIdentityCard")){
            method="个人信息上传";
        }
        log.info("请求URL:【{}】,机器号:【{}】,请求方式:【{}】,请求参数:【{}】,返回参数:【{}】",url,machno,method,reqParam,result);
    }

    private Object[] agrValue(Object[] _obj,Integer companyID,String ipAddr) throws Exception{
        for (Object obj : _obj) {
            agrMapValue(obj,companyID,ipAddr);
        }
        return _obj;
    }

    private Object agrMapValue(Object _obj,Integer companyID,String ipAddr) throws Exception{
        Map<String,Object> map = (Map<String,Object>) _obj;
        for (Map.Entry<String,Object> entry : map.entrySet()) {
            if (entry.getKey().equals("machNo")){
                String s = (String) map.get("machNo");
//                if (s.length()>8){
//                    String result = s.substring(s.length()-8,s.length());
//                    map.put("machNo",result);
//                }else {
                    map.put("machNo",s);
//                }

                map.put("companyID",companyID);
                map.put("ipAddr",ipAddr);
                break;
            }
        }
        return _obj;
    }

    private Map<String,Object> preHandle(Object[] args) throws Exception{
        Map<String,Object> mapObject=null;
        for (Object obj : args) {
            String strObj=JSON.toJSONString(obj);
            Map<String,Object> map = (Map<String, Object>) JSON.parse(strObj);
            for (Map.Entry<String,Object> entry : map.entrySet()) {
                if (entry.getKey().equals("sessionId")){
                    return agrMapValue(map);
                }
            }
        }
        return mapObject;
    }


    /*
     * @Description: 截取机器号
     * @Author: lu
     * @Date: 2022/3/11 9:39
     * @param map:
     * @return: java.util.Map<java.lang.String,java.lang.Object>
     **/
    private Map<String,Object> agrMapValue(Map<String,Object> map) throws Exception{
//        String s = (String) map.get("machNo");
//        if (s.length()>8){
//            String result = s.substring(s.length()-8,s.length());
//            map.put("machNo",result);
//        }else {
//            map.put("machNo",s);
//        }
        return map;
    }
    /**
     * 获取目标主机的ip
     * @param request
     * @return
     */
    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "10.68.132.244" : ip;
    }

    private Object changValue(Object _obj,int timeValue) throws Exception{
        //基本类型不作操作
        if(_obj instanceof Map){
            changeMapValue(_obj,timeValue);
        }else if(_obj instanceof List){
            @SuppressWarnings("unchecked")
            List<Object> list = (List<Object>) _obj;
            for (Object obj : list) {
                if(obj instanceof Map){
                    changeMapValue(_obj,timeValue);
                }else{
                    changObjectValue(_obj,timeValue);
                }
            }
        }else{
            changObjectValue(_obj,timeValue);
        }
        return _obj;
    }

    private Object changObjectValue(Object _obj,int timeValue) throws Exception{
        Class<?> resultClz = _obj.getClass();
        Field[] fieldInfo = resultClz.getDeclaredFields(); //获取class里的所有字段  父类字段获取不到    注：如果出现加密解密失败 请先查看idno是否在父类中
        for (Field field : fieldInfo) {
            if("".equals(field.getName())){
                field.setAccessible(true); //成员变量为private,故必须进行此操
                Object fieldValue = field.get(_obj);
                int afterValue = timeValue;

                field.set(_obj, afterValue);

                break;
            }
        }
        return _obj;
    }

    @SuppressWarnings("unchecked")
    private Object changeMapValue(Object _obj,int timeValue) throws Exception{
        JSONObject map = (JSONObject) _obj;
        map.put("serverTime",timeValue);
        return _obj;
    }
}
